-
Notifications
You must be signed in to change notification settings - Fork 83
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Pass-through options from DOM.context2d to getContext #273
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We shouldn’t code for specific options and should instead support passthrough. If options is an object, we pass it through to canvas.getContext, and we don’t add any try-catch around it. For backwards compatibility, if options is a number, it’s promoted to an options object {scale}.
Although, I’m also tempted to do nothing here. For the most part the DOM API in standard library is deprecated. It’s better to just have people write against the standard APIs so that when code is copy-pasted outside of Observable there are no gratuitous dependencies on the standard library.
It's fine to just do nothing. Easy enough to make this helper outside of stlib for special cases. (But it's very useful in general to not have to worry about dpr.) |
src/dom/context2d.js
Outdated
if (options == null) { | ||
options = {}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It’d be better to be strict here and only promote options to an object when it is undefined, ideally using default arguments (options = {}
). If the user passes in null explicitly, we should respect that and pass null to canvas.getContext.
src/dom/context2d.js
Outdated
if (options == +options) { | ||
if (options == null) { | ||
options = {}; | ||
} else if (options == +options) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be strict based on the type (typeof options === "number"
) rather than using coercion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Trying to be fully compatible with the current implementation, I wanted to support dpr passed as a string. Not sure why anyone would do that, though :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Full compatibility is not practical; someone could pass an object that implements valueOf, and this would have worked with the old API, but is ambiguous with named options under the new API. We should only support the intended pattern of passing a number.
src/dom/context2d.js
Outdated
scale = +options; | ||
options = {}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is probably fine, but it’d be slightly better to pass undefined, I think.
we might want to document the willReadFrequently boolean option as well? |
…ale, colorSpace}, allowing the creation of wide-gamut graphics on supported hardware and software. Note: this necessitates a try/catch because Safari will throw if the os requirements are not met. Tests/demo: https://observablehq.com/@fil/colorspace-display-p3-context2d
I believe the options handling can be simplified: export default function(width, height, options = {}) {
const {
scale = devicePixelRatio,
...contextOptions
} = !isNaN(options) ? {scale: options} : options;
const canvas = document.createElement("canvas");
canvas.width = width * scale;
canvas.height = height * scale;
canvas.style.width = width + "px";
const context = canvas.getContext("2d", contextOptions);
context.scale(scale, scale);
return context;
} |
const {scale = devicePixelRatio, ...contextOptions} = !isNaN(options) | ||
? {...(options != null && {scale: options})} | ||
: options; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const {scale = devicePixelRatio, ...contextOptions} = !isNaN(options) | |
? {...(options != null && {scale: options})} | |
: options; | |
const {scale = devicePixelRatio, ...contextOptions} = options && !isNaN(options) | |
? {scale: +options} | |
: options; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
0 is currently working as before (in the sense that it returns a canvas of size 0)—it would be simpler if we didn't support this "feature".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With the additional null check in the earlier comment we can remove options &&
here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(btw, that parseFloat was the result of a brainfart - I meant to write +options
)
@@ -41,9 +41,9 @@ html`<canvas width=960 height=500>` | |||
|
|||
If you are using [2D Canvas](https://www.w3.org/TR/2dcontext/) (rather than [WebGL](https://webglfundamentals.org/)), you should use [DOM.context2d](#DOM_context2d) instead of DOM.canvas for automatic pixel density scaling. | |||
|
|||
<a href="#DOM_context2d" name="DOM_context2d">#</a> DOM.<b>context2d</b>(<i>width</i>, <i>height</i>[, <i>dpi</i>]) [<>](https://github.com/observablehq/stdlib/blob/main/src/dom/context2d.mjs "Source") | |||
<a href="#DOM_context2d" name="DOM_context2d">#</a> DOM.<b>context2d</b>(<i>width</i>, <i>height</i>[, <i>options</i>]) [<>](https://github.com/observablehq/stdlib/blob/main/src/dom/context2d.mjs "Source") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't we list both signatures here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's there: "As a shorthand notation, options having only scale can be specified as a number." (could be phrased better?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel the shorthand should be mentioned earlier and emphasized, since it's the format that you'll encounter throughout Observable and the notation that you'll commonly use to disable dpr scaling.
canvas.height = height * dpi; | ||
export default function (width, height, options = {}) { | ||
const {scale = devicePixelRatio, ...contextOptions} = !isNaN(options) | ||
? {...(options != null && {scale: options})} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's no need for the destructuring here, and the null check should be explicit. If options is undefined, we already get {}
.
? {...(options != null && {scale: options})} | |
? options === null ? {} : {scale: options} |
Chrome (>= 111) supports display-p3 |
The third argument to DOM.context2d can be an options object with {scale, colorSpace}, allowing the creation of wide-gamut graphics on supported hardware and software.
Note: this necessitates a try/catch because Safari will throw if the os requirements are not met.
Tests/demo: https://observablehq.com/@fil/colorspace-display-p3-context2d
Draft, mostly for discussion.